/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org

This file is part of Gephi.

Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.visualization.screenshot;

import org.gephi.visualization.opengl.*;
import com.sun.opengl.util.FileUtil;
import com.sun.opengl.util.ImageUtil;
import com.sun.opengl.util.TileRenderer;
import java.awt.Cursor;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLPbuffer;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.gephi.ui.utils.DialogFileFilter;
import org.gephi.visualization.VizArchitecture;
import org.gephi.visualization.VizController;
import org.gephi.visualization.apiimpl.VizConfig;
import org.gephi.visualization.opengl.text.TextManager;
import org.gephi.visualization.swing.GLAbstractListener;
import org.gephi.visualization.swing.GraphDrawableImpl;
import org.netbeans.validation.api.ui.ValidationPanel;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
import org.openide.windows.WindowManager;

/**
 *
 * @author Mathieu Bastian
 */
public class ScreenshotMaker implements VizArchitecture {

    //Const
    private final String LAST_PATH = "ScreenshotMaker_Last_Path";
    private final String LAST_PATH_DEFAULT = "ScreenshotMaker_Last_Path_Default";
    private final String ANTIALIASING_DEFAULT = "ScreenshotMaker_Antialiasing_Default";
    private final String WIDTH_DEFAULT = "ScreenshotMaker_Width_Default";
    private final String HEIGHT_DEFAULT = "ScreenshotMaker_Height_Default";
    private final String TRANSPARENT_BACKGROUND_DEFAULT = "ScreenshotMaker_TransparentBackground_Default";
    private final String AUTOSAVE_DEFAULT = "ScreenshotMaker_Autosave_Default";
    private final String SHOW_MESSAGE = "ScreenshotMaker_Show_Message";
    //Architecture
    private GraphDrawableImpl drawable;
    private AbstractEngine engine;
    private TextManager textManager;
    private VizConfig vizConfig;
    //Settings
    private int antiAliasing = 2;
    private int width = 1024;
    private int height = 768;
    private boolean transparentBackground = false;
    private boolean finishedMessage = true;
    private boolean autoSave = false;
    private String defaultDirectory;
    //Running
    private File file;
    //State
    private boolean takeTicket = false;

    public ScreenshotMaker() {

        //Preferences
        String lastPathDefault = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH_DEFAULT, null);
        defaultDirectory = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH, lastPathDefault);
        antiAliasing = NbPreferences.forModule(ScreenshotMaker.class).getInt(ANTIALIASING_DEFAULT, antiAliasing);
        width = NbPreferences.forModule(ScreenshotMaker.class).getInt(WIDTH_DEFAULT, width);
        height = NbPreferences.forModule(ScreenshotMaker.class).getInt(HEIGHT_DEFAULT, height);
        transparentBackground = NbPreferences.forModule(ScreenshotMaker.class).getBoolean(TRANSPARENT_BACKGROUND_DEFAULT, transparentBackground);
        autoSave = NbPreferences.forModule(ScreenshotMaker.class).getBoolean(AUTOSAVE_DEFAULT, autoSave);
        finishedMessage = NbPreferences.forModule(ScreenshotMaker.class).getBoolean(SHOW_MESSAGE, finishedMessage);
    }

    public void initArchitecture() {
        drawable = VizController.getInstance().getDrawable();
        engine = VizController.getInstance().getEngine();
        textManager = VizController.getInstance().getTextManager();
        vizConfig = VizController.getInstance().getVizConfig();
    }

    public void takeScreenshot() {
        takeTicket = true;
    }

    private void take(File file) throws Exception {

        //System.out.println("Take Screenshot to " + file.getName());

        // Fix the image size for now
        int tileWidth = width / 16;
        int tileHeight = height / 12;
        int imageWidth = width;
        int imageHeight = height;

        //Caps
        GLCapabilities caps = new GLCapabilities();
        caps.setAlphaBits(8);
        caps.setDoubleBuffered(false);
        caps.setHardwareAccelerated(true);
        caps.setSampleBuffers(true);
        caps.setNumSamples(antiAliasing);

        //Buffer
        GLPbuffer pbuffer = GLDrawableFactory.getFactory().createGLPbuffer(caps, null, tileWidth, tileHeight, null);
        BufferedImage image = null;
        if (transparentBackground) {
            image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_4BYTE_ABGR);
        } else {
            image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR);
        }
        ByteBuffer imageBuffer = ByteBuffer.wrap(((DataBufferByte) image.getRaster().getDataBuffer()).getData());

        //Tile rendering
        TileRenderer tileRenderer = new TileRenderer();
        tileRenderer.setTileSize(tileWidth, tileHeight, 0);
        tileRenderer.setImageSize(imageWidth, imageHeight);
        if (transparentBackground) {
            tileRenderer.setImageBuffer(GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, imageBuffer);
        } else {
            tileRenderer.setImageBuffer(GL.GL_BGR, GL.GL_UNSIGNED_BYTE, imageBuffer);
        }
        tileRenderer.trPerspective(drawable.viewField, (float) imageWidth / (float) imageHeight, drawable.nearDistance, drawable.farDistance);

        //Get gl
        //GLContext oldContext = GLContext.getCurrent();
        GLContext context = pbuffer.getContext();
        if (context.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) {
            throw new RuntimeException("Error making pbuffer's context current");
        }
        GL gl = pbuffer.getGL();
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

        //Init
        drawable.initConfig(gl);
        vizConfig.setDisableLOD(true);
        engine.initScreenshot(gl, GLAbstractListener.glu);

        //Textrender - swap to 3D
        textManager.setRenderer3d(true);

        //Render in buffer
        do {
            tileRenderer.beginTile(gl);
            drawable.renderScreenshot(pbuffer);
        } while (tileRenderer.endTile(gl));

        //Clean
        context.release();
        pbuffer.destroy();

        //Textrender - back to 2D
        textManager.setRenderer3d(false);
        vizConfig.setDisableLOD(false);
        //Write image
        ImageUtil.flipImageVertically(image);
        writeImage(image);

        /*Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("png");
        if (iter.hasNext()) {
        ImageWriter writer = iter.next();
        ImageWriteParam iwp = writer.getDefaultWriteParam();
        //iwp.setCompressionType("DEFAULT");
        //iwp.setCompressionMode(javax.imageio.ImageWriteParam.MODE_EXPLICIT);
        //iwp.setCompressionQuality((int)(9*pngCompresssion));
        FileImageOutputStream output = new FileImageOutputStream(file);
        writer.setOutput(output);
        IIOImage img = new IIOImage(image, null, null);
        writer.write(null, img, iwp);
        writer.dispose();
        }*/

        //oldContext.makeCurrent();
    }

    private void writeImage(BufferedImage image) throws Exception {
        if (!autoSave) {
            //Get last directory
            String lastPathDefault = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH_DEFAULT, null);
            String lastPath = NbPreferences.forModule(ScreenshotMaker.class).get(LAST_PATH, lastPathDefault);
            final JFileChooser chooser = new JFileChooser(lastPath);
            chooser.setAcceptAllFileFilterUsed(false);
            chooser.setDialogTitle(NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.filechooser.title"));
            DialogFileFilter dialogFileFilter = new DialogFileFilter(NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.filechooser.pngDescription"));
            dialogFileFilter.addExtension("png");
            chooser.addChoosableFileFilter(dialogFileFilter);
            File selectedFile = new File(chooser.getCurrentDirectory(), getDefaultFileName() + ".png");
            chooser.setSelectedFile(selectedFile);
            int returnFile = chooser.showSaveDialog(null);
            if (returnFile != JFileChooser.APPROVE_OPTION) {
                return;
            }
            file = chooser.getSelectedFile();

            if (!file.getPath().endsWith(".png")) {
                file = new File(file.getPath() + ".png");
            }

            //Save last path
            defaultDirectory = file.getParentFile().getAbsolutePath();
            NbPreferences.forModule(ScreenshotMaker.class).put(LAST_PATH, defaultDirectory);

        } else {
            file = new File(defaultDirectory, getDefaultFileName() + ".png");
        }
        String format = "png";
        if (file != null) {
            format = FileUtil.getFileSuffix(file);
        }
        if (!ImageIO.write(image, format, file)) {
            throw new IOException("Unsupported file format");
        }
    }

    public void openglSignal(GLAutoDrawable drawable) {
        if (takeTicket) {
            takeTicket = false;
            try {
                beforeTaking();
                take(file);
                drawable.getContext().makeCurrent();
                afterTaking();
                file = null;
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private void beforeTaking() {
        WindowManager.getDefault().getMainWindow().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    }

    private void afterTaking() {
        WindowManager.getDefault().getMainWindow().setCursor(Cursor.getDefaultCursor());
        if (finishedMessage && file != null) {
            final String msg = NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.finishedMessage.message", file.getName());
            SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), msg, NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.finishedMessage.title"), JOptionPane.INFORMATION_MESSAGE);
                }
            });
        }
    }
    private static final String DATE_FORMAT_NOW = "HHmmss";

    private String getDefaultFileName() {

        Calendar cal = Calendar.getInstance();
        DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_NOW);
        String datetime = dateFormat.format(cal.getTime());

        return "screenshot_" + datetime;
    }

    public void configure() {
        ScreenshotSettingsPanel panel = new ScreenshotSettingsPanel();
        panel.setup(this);
        ValidationPanel validationPanel = ScreenshotSettingsPanel.createValidationPanel(panel);
        if (validationPanel.showOkCancelDialog(NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.configure.title"))) {
            panel.unsetup(this);
            return;
        }
//        DialogDescriptor dd = new DialogDescriptor(validationPanel, NbBundle.getMessage(ScreenshotMaker.class, "ScreenshotMaker.configure.title"));
//        Object result = DialogDisplayer.getDefault().notify(dd);
//        if (result == NotifyDescriptor.OK_OPTION) {
//            panel.unsetup(this);
//        }
    }

    public int getAntiAliasing() {
        return antiAliasing;
    }

    public void setAntiAliasing(int antiAliasing) {
        this.antiAliasing = antiAliasing;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public boolean isAutoSave() {
        return autoSave;
    }

    public void setAutoSave(boolean autoSave) {
        this.autoSave = autoSave;
    }

    public boolean isTransparentBackground() {
        return transparentBackground;
    }

    public void setTransparentBackground(boolean transparentBackground) {
        this.transparentBackground = transparentBackground;
    }

    public String getDefaultDirectory() {
        return defaultDirectory;
    }

    public void setDefaultDirectory(File directory) {
        if (directory != null && directory.exists()) {
            defaultDirectory = directory.getAbsolutePath();
            NbPreferences.forModule(ScreenshotMaker.class).put(LAST_PATH, defaultDirectory);
        }
    }
}
